﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

/*
* the header of the STSDirEntry is organized as follows:
* +-------------+------------+-----------+
* | Separator   | DirEntry   | Separator |
* +-------------+------------+-----------+
*/

namespace TSTreeSAX
{
    public class STSDirEntry
    {
        public STRTree myTree;                      // pointer to my TS-tree
        public int son;                            // block # of son
        public STSNode sonPtr;              		// pointer to son if in main mem.
        public bool sonIsData;                // TRUE, if son is a data page
        public int dimension;                      // dimension of the box
        public char[] bounces;                    // the mbr of the box
        public int sonLevel;                      // level of the node pointed to
        public int noOfData;                    // amount of data entries behind the
        // son of this entry    
        public char[] leftsep;                    // seperators
        public char[] rightsep;                   // seperators

        public int length;

        public STSDirEntry(int _dimension, bool _sonIsData, STRTree rt, int _length)
        {
            dimension = _dimension;
            sonIsData = _sonIsData;
            myTree = rt;
            bounces = new char[2 * dimension];
            leftsep = new char[dimension];
            rightsep = new char[dimension];
            length = _length;

            sonPtr = null;
            noOfData = 0;
           
        }
        /**
        * copy the contents of this to another Direntry object
        */
        public void copyTo(STSDirEntry target)
        {
            target.dimension = dimension;
            target.son = son;
            target.sonPtr = sonPtr;
            target.sonLevel = sonLevel;
            target.sonIsData = sonIsData;
            target.noOfData = noOfData;
     
            for (int i = 0; i < 2 * dimension; i++)
            {
                target.bounces[i] = bounces[i];
            }
            for (int j = 0; j < dimension; j++)
            {
                target.leftsep[j] = leftsep[j];
                target.rightsep[j] = rightsep[j];
            }
        }

        /**
        * Checks if point v is inside the entry's MBR
        */
        public bool isInside(char[] v)
        {
            int i;

            for (i = 0; i < dimension; i++)
            {
                if (v[i] < bounces[2 * i] || v[i] > bounces[2 * i + 1])
                    return false;
            }
            return true;
        }

      public bool IsBigger(char[] l1, char[] l2)
        {
            //∃j ≤ min{|a|, |b|} ∀i ∈ {1, . . . , j − 1} : ai = bi ∧ aj < bj 
            int n = Math.Min(l1.Count(x => x != '\0'), l2.Count(x => x != '\0'));

            if (l1[0] < l2[0]) return false;

            for (int i = 1; i < n; i++)
            {
                if (l1[i] > l2[i]) return true;
                else if (l1[i] < l2[i]) return false;
            }

            if (l1.Count(x => x != '\0') > l2.Count(x => x != '\0')) return false;
            else return true;
        }

       public bool IsSmaller(char[] l1, char[] l2)
        {
            //∃j ≤ min{|a|, |b|} ∀i ∈ {1, . . . , j − 1} : ai = bi ∧ aj < bj 
            int n = Math.Min(l1.Count(x => x != '\0'), l2.Count(x => x != '\0'));

            if (l1[0] > l2[0]) return false;

            for (int i = 1; i < n; i++)
            {
                if (l1[i] < l2[i]) return true;
                else if (l1[i] > l2[i]) return false;
            }

            if (l1.Count(x => x != '\0') > l2.Count(x => x != '\0')) return false;
            else return true;
        }

       //public float l_mindist_s(int startIndex, char[] q, char[] leftsep, char[] rightsep) 
       //{
       //    if (startIndex == leftsep.Count(i => i != '\0') - 1)
       //    {
       //        return Math.Abs(Utilities.SaxDistance(q[startIndex], leftsep[startIndex], length));
       //    }
       //    else
       //    {
       //        return Math.Abs(Utilities.SaxDistance(q[startIndex], leftsep[startIndex], length)) + leftMinDist(startIndex + 1, q, leftsep, rightsep);
       //    }
       //}

       //public float l_mindist_md(int startIndex, char[] q, char[] leftsep, char[] rightsep)
       //{
       //    float maxReal = Constants.MAXREAL;
       //   // char minchar = Constants.MINREALSAX;
       //    char maxchar = (char)((int)Constants.MINREALSAX + (length - 1));

       //    if((char)(((int)leftsep[startIndex]) + 1) < rightsep[startIndex])
       //    {
       //        return maxReal;
       //    }
       //    else if((char)(((int)leftsep[startIndex]) + 1) == rightsep[startIndex])
       //    {
       //        if(leftsep[startIndex + 1] <= rightsep[startIndex + 1]){
       //            return maxReal;
       //        }
       //        else{

       //            return Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)leftsep[startIndex]) + 1) > maxchar ? maxchar : (char)(((int)leftsep[startIndex]) + 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
       //        }
       //    }
       //    else
       //    {
       //        return Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)leftsep[startIndex]) + 1) > maxchar ? maxchar : (char)(((int)leftsep[startIndex]) + 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
       //    }
       //}
       public bool IsUseMDLeftDist(int startIndex, char[] leftsep, char[] rightsep)
       {
           if ((char)(((int)leftsep[startIndex]) + 1) < rightsep[startIndex])
           {
               return false;
           }
           else if ((char)(((int)leftsep[startIndex]) + 1) == rightsep[startIndex])
           {
               if (leftsep[startIndex + 1] <= rightsep[startIndex + 1])
               {
                   return false;
               }
               
           }

           return true;
       }
       public float leftMinDist(int startIndex, char[] q, char[] leftsep , char[] rightsep)
        {
            float f1,f2;
            f1 = Constants.MAXREAL;
            f2 = Constants.MAXREAL;
            char maxchar ;
            maxchar = (char)((int)Constants.MINREALSAX + (length - 1));
           // caculate f1
            if (startIndex == leftsep.Count(i => i != '\0') - 1)
            {
                f1 = Math.Abs(Utilities.SaxDistance(q[startIndex], leftsep[startIndex], length));
                
            }
            else
            {
                f1 = Math.Abs(Utilities.SaxDistance(q[startIndex], leftsep[startIndex], length)) + leftMinDist(startIndex + 1, q, leftsep, rightsep);
                //Console.WriteLine("left f1 :" + f1.ToString());
            }
           // caculate f2
            //if (startIndex == leftsep.Count(i => i != '\0') - 1
            //    || startIndex == rightsep.Count(i => i != '\0') - 1)
            //{
            //    f2 = Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
            //    Console.WriteLine("left f2 :" + f2.ToString());
            //}
            //else
            //{
            //    if (IsUseMDLeftDist(startIndex, leftsep, rightsep))
            //    {
            //        f2 = Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)leftsep[startIndex]) + 1) > maxchar ? maxchar : (char)(((int)leftsep[startIndex]) + 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
            //    }
            //    else
            //    {
            //        f2 = f1 + 1;
            //    }
            //}

            f2 = Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)leftsep[startIndex]) + 1) > maxchar ? maxchar : (char)(((int)leftsep[startIndex]) + 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
           // Console.WriteLine("left f2 :" + f2.ToString());

            if (f1 == Constants.MAXREAL)
            {
                if (f2 == Constants.MAXREAL) return 0;
                else return f2;
            }
            else
            {
                if (f2 == Constants.MAXREAL) return f1;
                else return Math.Min(f1, f2);
            }
            
        }


      
       //public float r_mindist_s(int startIndex, char[] q, char[] rightsep)
       //{      
       //    if (startIndex == rightsep.Count(i => i != '\0') - 1)
       //    {
       //        return Math.Abs(Utilities.SaxDistance(q[startIndex], rightsep[startIndex], length));
       //    }
       //    else
       //    {
       //        return Math.Abs(Utilities.SaxDistance(q[startIndex], rightsep[startIndex], length)) + r_mindist_s(startIndex + 1, q, rightsep);
       //    }

       //}
       //public float r_mindist_md(int startIndex, char[] q, char[] leftsep, char[] rightsep)
       //{
       //    float maxReal = Constants.MAXREAL;
       //    char minchar = Constants.MINREALSAX;
       //  //  char maxchar  = Convert.ToChar((int)Constants.MINREALSAX + (length - 1) + 65);

       //    if ((char)(((int)rightsep[startIndex]) + 1) < leftsep[startIndex])
       //    {
       //        return maxReal;
       //    }
       //    else if ((char)(((int)rightsep[startIndex]) + 1) == leftsep[startIndex])
       //    {
       //        if (rightsep[startIndex + 1] <= leftsep[startIndex + 1])
       //        {
       //            return maxReal;
       //        }
       //        else
       //        {
       //            return Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)rightsep[startIndex]) - 1) < minchar? minchar : (char)(((int)rightsep[startIndex]) - 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
       //        }
       //    }
       //    else
       //    {
       //        return Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)rightsep[startIndex]) - 1) < minchar ? minchar : (char)(((int)rightsep[startIndex]) - 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
       //    }
       //}

       public bool IsUseMDRightDist(int startIndex, char[] leftsep, char[] rightsep)
       {
           if ((char)(((int)rightsep[startIndex]) + 1) < leftsep[startIndex])
           {
               return false;
           }
           else if ((char)(((int)rightsep[startIndex]) + 1) == leftsep[startIndex])
           {
               if (rightsep[startIndex + 1] <= leftsep[startIndex + 1])
               {
                   return false;
               }
           }
           return true;

       }
      public float rightMinDist(int startIndex, char[] q, char[] leftsep, char[] rightsep)
       {
           float f1, f2;
           char minchar = Constants.MINREALSAX;
           f1 = Constants.MAXREAL;
           f2 = Constants.MAXREAL;
          //caculate f1
           if (startIndex == rightsep.Count(i => i != '\0') - 1)
           {
               f1 = Math.Abs(Utilities.SaxDistance(q[startIndex], rightsep[startIndex], length));
              
           }
           else
           {
               f1 =  Math.Abs(Utilities.SaxDistance(q[startIndex], rightsep[startIndex], length)) + rightMinDist(startIndex + 1, q, rightsep, rightsep);
           }
          // caculate f2
           //if (startIndex == leftsep.Count(i => i != '\0') - 1
           //     || startIndex == rightsep.Count(i => i != '\0') - 1)
           //{
           //    Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
           //}
           //else
           //{
           //    if (IsUseMDRightDist(startIndex, leftsep, rightsep))
           //    {
           //        f2 = Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)rightsep[startIndex]) - 1) < minchar ? minchar : (char)(((int)rightsep[startIndex]) - 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
           //    }
           //    else
           //    {
           //        f2 = f1 + 1;
           //    }
           //}

           f2 = Math.Abs(Utilities.SaxDistance(q[startIndex], (char)(((int)rightsep[startIndex]) - 1) < minchar ? minchar : (char)(((int)rightsep[startIndex]) - 1), length)) + Utilities.DTWdistance(dimension, startIndex, length, q, this.bounces);
          
       
           if (f1 == Constants.MAXREAL)
           {
               if (f2 == Constants.MAXREAL) return 0;
               else return f2;
           }
           else
           {
               if (f2 == Constants.MAXREAL) return f1;
               else return Math.Min(f1, f2);
           }
       }
      

        public float minDist(char[] point)
        {
            float md = 0;
          
                if (IsSmaller(point, this.leftsep))
                {
                    md += leftMinDist(0, point, this.leftsep, this.rightsep);

                   // Console.WriteLine("left md :" + md.ToString());

                }
                else if (IsBigger(point, this.rightsep))
                {
                    md += rightMinDist(0, point, this.leftsep, this.rightsep);
                   // Console.WriteLine("right md :" + md.ToString());
                }
                else
                {
                    md += 0;
                }

                // Console.WriteLine("md :" + md.ToString());
            return md;
        }

        /**
         * Tests if the parameter mbr is inside or overlaps the MBR of the entry
         * Constants.INSIDE :inside
         * Constants.OVERLAP: overlap
         * Constants.S_NONE: None
        */
        public int interSection(char[] mbr)
        {
            bool inside;
            bool overlap;
            int i;

            overlap = true;
            inside = true;
            for (i = 0; i < dimension; i++)
            {
                if (mbr[2 * i] > bounces[2 * i + 1] || mbr[2 * i + 1] < bounces[2 * i])
                    overlap = false;
                if (mbr[2 * i] < bounces[2 * i] || mbr[2 * i + 1] > bounces[2 * i + 1])
                    inside = false;
            }
            if (inside)
                return Constants.INSIDE;
            else if (overlap)
                return Constants.OVERLAP;
            else
                return Constants.S_NONE;
        }
        /**
       * reads from the input stream the object's info
       * used by STSDirNode.readFromBuffer()
       */
        public void readFromBuffer(BinaryReader din)
        {
            for (int i = 0; i < 2 * dimension; ++i)
                bounces[i] = din.ReadChar();
            son = din.ReadInt32();
            noOfData = din.ReadInt32();
        }
        /**
    * writes to the output stream the object's info
    * used by STSDirNode.write_to_buffer()
    */
        public void writeToBuffer(BinaryWriter dout)
        {
            for (int i = 0; i < 2 * dimension; ++i)
                dout.Write(bounces[i]);
            dout.Write(son);
            dout.Write(noOfData);
        }
        /************************************************************************/
        /* Get the size in number of bytes of a DirEntry                                                                      */
        /************************************************************************/
        public int getSize()
        {
            return 2 * dimension * Constants.SIZEOF_CHAR + Constants.SIZEOF_INT
                            + Constants.SIZEOF_INT;
        }

        public int getleftSepLengh()
        {
            for (int i = 0; i < dimension; i++)
            {
                if (leftsep[i] == null) return i;
            }
            return dimension;
        }
        public int getrightSepLengh()
        {
            for (int i = 0; i < dimension; i++)
            {
                if (rightsep[i] == null) return i;
            }
            return dimension;
        }
        /**
        * returns the son_ptr (the node this entry points to
        * if the node is not in main memory, it it read
        * from disk (see STSDirNode/RTDataNode constructor)
        */
        public STSNode getSon()
        {
            if (sonPtr == null)
            {
                if (sonIsData)
                    sonPtr = new STSDataNode(myTree, son, length);
                else
                    sonPtr = new STSDirNode(myTree, son, length);
            }
            return sonPtr;
        }
        /**
        * returns true if the entry intersects the circle
        */
        public  bool isInterSecCircle(STSPoint center, float radius)
        {
            return Utilities.isCircleInterSection(dimension, bounces, center, radius,length);
        }
        /**
   * returns true if the entry intersects the ring
   */
        public bool isRingInterSection(STSPoint center, float radius1, float radius2)
        {
            return Utilities.isRingInterSection(dimension, bounces, center, radius1, radius2,length);
        }
        public void delete()
        {
            if (sonPtr != null)
                ((INode)sonPtr).delete();
        }

    }
}
